﻿#include "utils.h"

namespace my_util_nets {

    char *host2ip(const char *hostname, const char *port) {
        struct addrinfo  hints;
        struct addrinfo *result, *rp;
        int              s;
        char             host[NI_MAXHOST];
        char             serv[NI_MAXSERV];

        memset(&hints, 0, sizeof(struct addrinfo));
        hints.ai_family   = AF_UNSPEC;   /* Allow IPv4 or IPv6 */
        hints.ai_socktype = SOCK_STREAM; /* Datagram socket */
        hints.ai_flags    = 0;
        hints.ai_protocol = IPPROTO_TCP; /* Any protocol */
        s                 = getaddrinfo(hostname, port, &hints, &result);
        if (s != 0) {
            printf("getaddrinfo: %s\n", gai_strerror(s));
            return NULL;
        }
        for (rp = result; rp != NULL; rp = rp->ai_next) {

            s = getnameinfo(rp->ai_addr, (socklen_t)rp->ai_addrlen,
                            host, sizeof(host),
                            serv, sizeof(serv),
                            NI_NUMERICHOST | NI_NUMERICSERV);
            if (s != 0) {
                printf("getnameinfo: %s\n", gai_strerror(s));
                freeaddrinfo(result);
                return NULL;
            }
            printf("Host: %s, Service: %s\n", host, serv);

            if (result->ai_family == AF_INET) { // IPv4
                sockaddr_in *ipv4 = (sockaddr_in *)result->ai_addr;
                char         ipstr[INET_ADDRSTRLEN];
                inet_ntop(result->ai_family, &(ipv4->sin_addr), ipstr, sizeof(ipstr));
                std::cout << "IPv4 Address: " << ipstr << std::endl;
                return ipstr;
            }
            // else if (result->ai_family == AF_INET6) { // IPv6
            //             sockaddr_in6 *ipv6 = (sockaddr_in6 *)result->ai_addr;
            //             char          ipstr[INET6_ADDRSTRLEN];
            //             inet_ntop(result->ai_family, &(ipv6->sin6_addr), ipstr, sizeof(ipstr));
            //             std::cout << "IPv6 Address: " << ipstr << std::endl;
            //             return ipstr;
            //         }
        }
        freeaddrinfo(result);
        return host;
    }

    int host2ip(const char *hostname) {
        struct hostent *host_entry;
        char           *ip;
        // 获取主机信息
        host_entry = gethostbyname(hostname);
        if (host_entry == NULL) {
            std::cout << "Get Host By Name Failed: " << WSAGetLastError() << std::endl;
            return -1;
        }
        // 将第一个IP地址转换为字符串
        ip = inet_ntoa(*(struct in_addr *)host_entry->h_addr_list[0]);
        if (ip == NULL) {
            std::cout << "Inet Ntoa Failed" << std::endl;
            return -1;
        }
        std::cout << "Hostname: " << hostname << ", IP Address: " << ip << std::endl;
        return 0;
    }

    int un_compress(const char *src, size_t srcLen, const char *dest, size_t destLen) {
        z_stream strm;
        strm.zalloc    = NULL;
        strm.zfree     = NULL;
        strm.opaque    = NULL;
        strm.avail_in  = srcLen;
        strm.avail_out = destLen;
        strm.next_in   = (Bytef *)src;
        strm.next_out  = (Bytef *)dest;

        int err = -1, ret = -1;
        err = inflateInit2(&strm, MAX_WBITS + 16);

        if (err == Z_OK) {
            err = inflate(&strm, Z_FINISH);
            if (err == Z_STREAM_END) {
                ret = strm.total_out;
            } else {
                inflateEnd(&strm);
                return err;
            }
        } else {
            inflateEnd(&strm);
            return err;
        }

        inflateEnd(&strm);
        return err;
    }

    // 执行SSL握手
    int perform_ssl_handshake(SSL *ssl, int sockfd) {
        //  SSL *ssl：OpenSSL 的 SSL 会话对象
        //  int sockfd：底层 TCP 套接字文件描述符

        int       ssl_ret;         //  存储 SSL_connect() 的返回值
        int       retry_count = 0; //  当前重试次数计数器
        const int max_retries = 3; //  最多三次重试

        while ((ssl_ret = SSL_connect(ssl)) != 1) {    //  SSL_connect(ssl) 返回值不等于 1（握手未完成）
            int ssl_err = SSL_get_error(ssl, ssl_ret); //  SSL_get_error() 解析具体错误代码

            switch (ssl_err) {
            //  以下两个错误都执行同样的代码
            case SSL_ERROR_WANT_READ:
            case SSL_ERROR_WANT_WRITE: {
                // 等待套接字可读/可写
                fd_set fdset;
                FD_ZERO(&fdset);
                FD_SET(sockfd, &fdset);

                struct timeval tv;
                tv.tv_sec  = 2;
                tv.tv_usec = 0;

                int select_ret = select(sockfd + 1,
                                        (ssl_err == SSL_ERROR_WANT_READ) ? &fdset : NULL,
                                        (ssl_err == SSL_ERROR_WANT_WRITE) ? &fdset : NULL,
                                        NULL, &tv);
                //  这是分支填写，确定自己对哪个 I/O 感兴趣而后监听，没错误就是成功握手，有错误就是超时了，现在挂起线程等他们至多 2 秒的响应

                if (select_ret <= 0) {
                    fprintf(stderr, "SSL handshake timeout or error\n");
                    return 0;
                }
            } break;

            case SSL_ERROR_SYSCALL:
                if (errno == EAGAIN || errno == EWOULDBLOCK) {
                    //  EWOULDBLOCK 是操作阻塞， EAGAIN 表示重试资源
                    if (++retry_count > max_retries) {
                        fprintf(stderr, "SSL handshake max retries exceeded\n");
                        return 0;
                    }
                    Sleep(100000); // 100ms
                    break;
                }
                // 其他错误直接退出
                return 0;

            default:
                fprintf(stderr, "SSL handshake failed with error %d\n", ssl_err);
                ERR_print_errors_fp(stderr);
                return 0;
            }
        }
        return 1; //  握手完成，密钥互通
    }

    // 解析URL，提取协议、主机、端口和路径
    void parse_url(const char *url, const char **protocol, char **host, const char **path, int *port) {
        char *url_copy = strdup(url); // 它会————分配一块动态内存————来存储字符串的副本，并返回指向这块内存的指针。
        char *p        = url_copy;

        // 提取协议
        *protocol = "http";
        if (strstr(url, "https://") == url) {
            *protocol = "https";
            p += 8;
            *port = 443;
        } else if (strstr(url, "http://") == url) {
            p += 7;
            *port = 80;
        }

        // 提取主机和路径
        *host       = p;
        char *slash = strchr(p, '/');
        if (slash) {
            *slash = '\0';      // 分隔主机和路径
            *path  = slash + 1; // 相对路径，或者资源
        } else {
            *path = "/";
        }

        // 检查是否包含端口号
        char *colon = strchr(*host, ':');
        if (colon) {
            *colon = '\0';
            *port  = atoi(colon + 1); // atoi 是 C 语言标准库中的一个函数，用于将字符串转换为意思整数。它的全称是 "ASCII to Integer"，即“将 ASCII 字符串转换为整数”。
        }

        // 创建副本并释放临时内存
        // strdup 是一个在 C 语言中常用的函数，用于复制一个字符串。它的名字来源于 "string duplicate"，即“字符串复制”。
        // 它会————分配一块动态内存————来存储字符串的副本，并返回指向这块内存的指针。
        *host = strdup(*host);
        *path = strdup(*path);
        free(url_copy);
    }

    // 从响应头中提取Location
    char *get_location(const char *response) {
        const char *location_start = strstr(response, "Location: ");
        if (!location_start)
            return NULL;

        location_start += 10; // 跳过"Location: "
        const char *location_end = strstr(location_start, "\r\n");
        if (!location_end)
            return NULL;

        size_t len      = location_end - location_start;
        char  *location = (char *)malloc(len + 1);
        strncpy(location, location_start, len);
        location[len] = '\0';

        return location;
    }

    // 获取HTTP状态码
    int get_status_code(const char *response) {
        int  status_code      = 0;
        char status_line[128] = {0};
        sscanf(response, "HTTP/%*[^ ] %d", &status_code); //  从字符串 response 中提取 HTTP 状态码
        return status_code;
    }

    // 创建套接字（支持指定端口）
    int create_socket(char *ip, int port) {
        int sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd < 0) {
            perror("socket creation failed");
            return -1;
        }

        struct sockaddr_in sin = {0};
        sin.sin_family         = AF_INET;
        sin.sin_port           = htons(port);
        if (inet_pton(AF_INET, ip, &sin.sin_addr) <= 0) { //  点分十进制改成 16 进制的 IP
            perror("Invalid address");
            closesocket(sockfd);
            return -1;
        }

        if (0 != connect(sockfd, (struct sockaddr *)&sin, sizeof(struct sockaddr_in))) {
            perror("connection failed");
            closesocket(sockfd);
            return -1;
        }

        // 设置为非阻塞模式 TODO
        // fcntl(sockfd, F_SETFL, O_NONBLOCK);
        return sockfd;
    }
}